package ru.spbu.math.android.cbrf.charts;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Bundle;

import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import ru.spbu.math.android.cbrf.R;
import ru.spbu.math.android.cbrf.charts.library.chart.PointStyle;
import ru.spbu.math.android.cbrf.charts.library.chart.TimeChart;
import ru.spbu.math.android.cbrf.charts.library.chart.renderer.XYMultipleSeriesRenderer;
import ru.spbu.math.android.cbrf.charts.library.chart.renderer.XYSeriesRenderer;
import ru.spbu.math.android.cbrf.charts.library.model.XYMultipleSeriesDataset;
import ru.spbu.math.android.cbrf.charts.library.model.XYSeries;
import ru.spbu.math.android.cbrf.dataprovider.DataProvider;
import ru.spbu.math.android.cbrf.dataprovider.IllegalStockItemsCombinationException;
import ru.spbu.math.android.cbrf.dataprovider.Validator;
import ru.spbu.math.android.cbrf.model.*;

import java.util.*;

/**
 * @author Nikita
 */
public class ChartsActivity extends Activity {

	
	/**
	 * @uml.property name="startDate"
	 */
	private Calendar startDate;

	/**
	 * @uml.property name="endDate"
	 */
	private Calendar endDate;
	private  double minValue = Double.MAX_VALUE;
	private  double maxValue = Double.MIN_VALUE;

	private  List<String> titles = new ArrayList<String>();
	private  List<Date[]> dates = new ArrayList<Date[]>();
	private  List<double[]> values = new ArrayList<double[]>();
	/**
	 * @uml.property name="styles"
	 * @uml.associationEnd multiplicity="(0 -1)"
	 */
	private  PointStyle[] styles = new PointStyle[0];
	private  int[] colors = new int[0];
	private AlertDialog addAlret;

	private AlertDialog deleteAlret;
	private  List<RateType> chartsSorceCode = new LinkedList<RateType>();
	private AlertDialog.Builder deleteChartBuilder;

	private AlertDialog.Builder addChartBuilder;
	private  CharSequence[] deleteItems;

	/**
	 * @uml.property name="chart"
	 * @uml.associationEnd
	 */
	private  TimeChart chart;
	/**
	 * @uml.property name="dateDialog"
	 * @uml.associationEnd
	 */
	private DateDialog dateDialog;
	private List<RateType> RateTypeList;

	private DialogInterface.OnClickListener alretDeleteListener;

	private DialogInterface.OnClickListener alretAddListener;

	private String nominalName = "Rub";
	private String source = "USD";
	private String target = "RUB";


	private DataProvider dataProvider = DataProvider.getInstance(this);
	private DataProvider.RateTypeGroups enumRateTypeGroups = DataProvider.RateTypeGroups.Currency;

	/**
	 * @uml.property name="graphicalView"
	 * @uml.associationEnd
	 */
	private GraphicalView graphicalView;

	/**
	 * Called when the activity is first created.
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	
		setContentView(R.layout.charts_layout);
		Bundle extras = getIntent().getExtras();

		source = extras.getString("SOURCE");
		target = extras.getString("TARGET");
		Log.d("bnv Source", source);

		endDate = new GregorianCalendar();
		startDate = new GregorianCalendar(endDate.get(Calendar.YEAR), endDate.get(Calendar.MONTH), endDate
				.get(Calendar.DAY_OF_MONTH) - 30);

		Validator validator = new Validator();
		enumRateTypeGroups = Validator.getRateTypeGroupsbySourceTarget(source, target);
		StockItemStore sourseItem = StockItemStore.Instance();
		RateTypeList = validator.getRateTypes(enumRateTypeGroups);
		CharSequence[] addItems = new CharSequence[RateTypeList.size()];
		for (int i = 0; i < RateTypeList.size(); i++)
			addItems[i] =  sourseItem.findByCode(RateTypeList.get(i).getSourceCode()).getName();//RateTypeList.get(i).getSourceCode();

		nominalName = sourseItem.findByCode(target).getnominalName();
		if (nominalName == "") nominalName = "Rub";
		alretDeleteListener = new alretDeleteListener();
		alretAddListener = new alretAddListener();

		addChartBuilder = new AlertDialog.Builder(this);
		addChartBuilder.setTitle("Pick a chart");
		addChartBuilder.setItems(addItems, alretAddListener);
		addAlret = addChartBuilder.create();

		addChartBySorceTarget(source, target);
		chartsSorceCode.add(new RateType(source, target));

		deleteChartBuilder = new AlertDialog.Builder(this);
		deleteChartBuilder.setTitle("Delete a chart");
		deleteChartBuilder.setItems(getDeleteItem(), alretDeleteListener);
		deleteAlret = deleteChartBuilder.create();
	}

	protected void onResume() {
		super.onResume();
		Log.d("bnv", "onResume");
	
		if (graphicalView == null) {
			graphicalView = getTimeChartView(getDataset(), getRenderer());
	
		setContentView(graphicalView);

		} else {			
			Log.d("bnv", "onResume2");
			graphicalView.repaint();
			//setContentView(l);		
		}
	}

	public boolean onKeyDown(int keyCode, KeyEvent event) {
		super.onKeyDown(keyCode, event);
		
		final int range = 3;

		Log.d("keyCode", "" + keyCode);
		if (keyCode == 21) {
			startDate.set(startDate.get(Calendar.YEAR), startDate.get(Calendar.MONTH), startDate
					.get(Calendar.DAY_OF_MONTH)
					- range);
			endDate.set(endDate.get(Calendar.YEAR), endDate.get(Calendar.MONTH), endDate.get(Calendar.DAY_OF_MONTH)
					- range);
			setNewData();
		}
		if (keyCode == 22) {
			Calendar start = getStartDate();
			start.set(start.get(Calendar.YEAR), start.get(Calendar.MONTH), start.get(Calendar.DAY_OF_MONTH) + range);
			Calendar end = getEndDate();
			end.set(end.get(Calendar.YEAR), end.get(Calendar.MONTH), end.get(Calendar.DAY_OF_MONTH) + range);
			if (end.before(new GregorianCalendar()))
				setDateRange(start, end);
		}
		return false;

	}
	
	protected void  onStop(){
		super.onStop();
		Log.d("bnv", " onStop");
		//finish();
	}
	
	protected void  onStart(){
		super.onStart();
		Log.d("bnv", " onStart");
			
	}

	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		menu.add(0, ADD_CHART, 0, getString(R.string.Add_chart)).setIcon(R.drawable.plus);
		menu.add(0, SET_DATE, 0, getString(R.string.Set_Date));
		menu.add(0, DELETE_CHART, 0, getString(R.string.Delete_chart)).setIcon(R.drawable.minus);
		return true;
	}

	public boolean onOptionsItemSelected(MenuItem item) {
		super.onOptionsItemSelected(item);
		switch (item.getItemId()) {
			case ADD_CHART:
				addAlret.show();
				return true;
			case DELETE_CHART:
				deleteChart();
				return true;
			case SET_DATE:
				if (dateDialog == null)
					dateDialog = new DateDialog(this, this);
				dateDialog.show();

				return true;
		}
		return false;
	}

	private GraphicalView getTimeChartView(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) {
		checkParameters(dataset, renderer);
		chart = new TimeChart(dataset, renderer);
		chart.setDateFormat(DATA_FORMAT);
		return new GraphicalView(ChartsActivity.this, chart, this);
	}

	private void checkParameters(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer) {
		if (dataset == null || renderer == null || dataset.getSeriesCount() != renderer.getSeriesRendererCount()) {
			throw new IllegalArgumentException(
					"Dataset and renderer should be not null and should have the same number of series");
		}
	}

	public void addChartBySorceTarget(String source, String target) {
		StockItemStore sourseItem = StockItemStore.Instance();

		try {
			addChart(Arrays.asList(dataProvider.getDynamicRates(sourseItem.findByCode(source), sourseItem
					.findByCode(target), startDate.getTime(), endDate.getTime())));
		} catch (IllegalStockItemsCombinationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// addChart(cbrfData.getDynamicCountRateList(sourseItem.findByCode(source),
		// sourseItem.findByCode(target),
		// startDate.getTime(), endDate.getTime(), 5));

	}

	public void addChart(List<Rate> rateData) {
		titles.add(rateData.get(0).getSource().getName() + " (" + rateData.get(0).getSource().getNominalValue() + ")");
		//nominalName = "" + rateData.get(0).getSource().getnominalName();

		styles = new PointStyle[styles.length + 1];
		Arrays.fill(styles, PointStyle.CIRCLE);

		int[] colors2 = new int[colors.length + 1];
		for (int i = 0; i < colors.length; i++)
			colors2[i] = colors[i];
		colors2[colors.length] = generateRandomColor();
		colors = colors2;

		addChartData(rateData);
	}

	private void addChartData(List<Rate> rateData) {
		Date[] dateArr = new Date[rateData.size()];
		double[] valuesArr = new double[rateData.size()];
		for (int i = 0; i < rateData.size(); i++) {
			dateArr[i] = rateData.get(i).getDate();
			valuesArr[i] = rateData.get(i).getValue();
			if (valuesArr[i] > maxValue)
				maxValue = valuesArr[i];
			if (valuesArr[i] < minValue)
				minValue = valuesArr[i];
		}
		maxValue *= 1.05;
		minValue *= 0.95;
		dates.add(dateArr);
		values.add(valuesArr);
	}

	private void deleteChart() {
		deleteChartBuilder.setItems(getDeleteItem(), alretDeleteListener);
		deleteAlret = deleteChartBuilder.create();
		deleteAlret.show();
	}

	private void deleteChartByCode(String code) {
		//Log.d("bnv deleteChartByCode", code);
		RateType rt = new RateType(code, target);
		int index = chartsSorceCode.indexOf(rt);
		chartsSorceCode.remove(index);
		titles.remove(index);
		dates.remove(index);
		values.remove(index);
		int[] colors2 = new int[colors.length - 1];
		for (int i = 0; i < colors.length - 1; i++)
			colors2[i] = colors[i];
		colors = colors2;

		setNewMinMaxValue();

	}

	private void setNewMinMaxValue() {
		maxValue = Double.MIN_VALUE;
		minValue = Double.MAX_VALUE;
		for (int i = 0; i < values.size(); i++) {
			for (double j : values.get(i)) {
				if (j > maxValue)
					maxValue = j;
				if (j < minValue)
					minValue = j;
			}
		}
		maxValue *= 1.05;
		minValue *= 0.95;

	}

	public void updateChartsData() {
		chart.setDataset(getDataset());
		chart.setRenderer(getRenderer());
		graphicalView.repaint();
	}

	public void setDateRange(Calendar start, Calendar end) {
		startDate = start;
		endDate = end;
		setNewData();
	}

	/**
	 * @return
	 * @uml.property name="startDate"
	 */
	public Calendar getStartDate() {
		return startDate;
	}

	/**
	 * @return
	 * @uml.property name="endDate"
	 */
	public Calendar getEndDate() {
		return endDate;
	}

	// TODO
	private void setNewData() {
		values.clear();
		dates.clear();
		StockItemStore sourseItem = StockItemStore.Instance();
		for (RateType type : chartsSorceCode) {
			try {
				addChartData(Arrays.asList(dataProvider.getDynamicRates(sourseItem.findByCode(type.getSourceCode()), sourseItem
						.findByCode(target), startDate.getTime(), endDate.getTime())));
			} catch (IllegalStockItemsCombinationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		setNewMinMaxValue();
		updateChartsData();
	}

	private CharSequence[] getDeleteItem() {
		if (chartsSorceCode.size() > 1) {
			deleteItems = new CharSequence[chartsSorceCode.size()];
			for (int i = 0; i < chartsSorceCode.size(); i++) {
				deleteItems[i] = StockItemStore.Instance().findByCode(chartsSorceCode.get(i).getSourceCode()).getName();
				// deleteItems = chartsSorceCode.toArray(deleteItems);
			}
		} else
			deleteItems = new CharSequence[0];
		return deleteItems;
	}

	DateValue getDateValueByXY(double x, double y) {
		int width = graphicalView.getWidth() - 20;
		int height = graphicalView.getHeight() * 4 / 5;

		Date date = null;
		double value = 0;
		long dateRange = endDate.getTimeInMillis() - startDate.getTimeInMillis();
		double valueRange = maxValue - minValue;
		long findDate = startDate.getTimeInMillis() + (long) ((x - 20) * dateRange / width);
		double findValue = minValue + (double) ((height - y) * valueRange) / height;
		final double eps = dateRange * 0.03, epsy = valueRange * 0.03;
		boolean ok = false;
		int i = 0;
		for (Date[] d : dates) {
			for (i = 0; i < d.length; i++) {

				if ((d[i].getTime() < findDate + eps) && (d[i].getTime() > findDate - eps)) {
					date = d[i];
					ok = true;
					break;
				}
				if (ok)
					break;
			}
			ok = false;
		}
		for (double[] v : values)
			if ((i < v.length) && (v[i] < findValue + epsy) && (v[i] > findValue - epsy)) {
				value = v[i];
				break;
			}

		if (date == null || value == 0)
			return null;
		return new DateValue(date, value);
	}
	/**
	 * Builds an XY multiple time dataset using the provided values.
	 */
	private XYMultipleSeriesDataset buildDateDataset(String[] titles, List<Date[]> xValues, List<double[]> yValues) {
		XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset();
		int length = titles.length;
		for (int i = 0; i < length; i++) {
			XYSeries series = new XYSeries(titles[i]);
			Date[] xV = xValues.get(i);
			double[] yV = yValues.get(i);
			int seriesLength = xV.length;
			for (int k = 0; k < seriesLength; k++) {
				series.add(xV[k].getTime(), yV[k]);
			}
			dataset.addSeries(series);
		}
		return dataset;
	}

	/**
	 * Builds an XY multiple series renderer
	 */
	private XYMultipleSeriesRenderer buildRenderer() {
		XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
		int length = colors.length;
		for (int i = 0; i < length; i++) {
			XYSeriesRenderer r = new XYSeriesRenderer();
			r.setColor(colors[i]);
			r.setPointStyle(styles[i]);
			r.setFillPoints(true);
			renderer.addSeriesRenderer(r);
		}
		return renderer;
	}

	private void setChartSettings(XYMultipleSeriesRenderer renderer, String title, String xTitle, String yTitle) {
		renderer.setChartTitle(title);
		renderer.setXTitle(xTitle);
		renderer.setYTitle(yTitle);

		// TODO
		renderer.setXAxisMin(dates.get(0)[0].getTime());
		renderer.setXAxisMax(dates.get(0)[dates.get(0).length - 1].getTime());
		renderer.setYAxisMin(minValue);
		renderer.setYAxisMax(maxValue);
		renderer.setAxesColor(Color.GRAY);
		renderer.setLabelsColor(Color.LTGRAY);
		renderer.setAntialiasing(true);
		renderer.setShowLabels(true);
		renderer.setShowGrid(true);
	}

	// TODO:

	/**
	 * Generates a random color of chart
	 */
	public int generateRandomColor() {
		double rand = Math.random();
		int color = Color.WHITE;
		if (rand < 0.2)
			color = Color.BLUE;
		else if (rand < 0.4)
			color = Color.CYAN;
		else if (rand < 0.6)
			color = Color.RED;
		else if (rand < 0.8)
			color = Color.MAGENTA;
		else if (rand < 1)
			color = Color.GREEN;

		return color;
	}

	/**
	 * Returns a Dataset with default settings
	 * 
	 * @return the XYMultipleSeriesDataset with default settings
	 */
	private XYMultipleSeriesDataset getDataset() {
		String[] titlesArr = new String[titles.size()];
		titlesArr = titles.toArray(titlesArr);
		return buildDateDataset(titlesArr, dates, values);
	}

	/**
	 * Returns a Renderer with default settings
	 * 
	 * @return the XYMultipleSeriesRenderer with default settings
	 */
	// TODO:
	private XYMultipleSeriesRenderer getRenderer() {
		XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
		renderer = buildRenderer();

		setChartSettings(renderer, "", "", nominalName);
		renderer.setYLabels(10);
		return renderer;
	}

	private final String DATA_FORMAT = "dd MMM";
	private final int ADD_CHART = 1;
	private final int DELETE_CHART = 2;
	private final int SET_DATE = 3;

	private class alretDeleteListener implements DialogInterface.OnClickListener {

		// TODO
		public void onClick(DialogInterface dialog, int item) {
			deleteChartByCode(chartsSorceCode.get(item).getSourceCode());
			updateChartsData();
			graphicalView.repaint();

		}
	}

	private class alretAddListener implements DialogInterface.OnClickListener {

		public void onClick(DialogInterface dialog, int item) {
			addChartBySorceTarget(RateTypeList.get(item).getSourceCode(), RateTypeList.get(item).getTargetCode());
			chartsSorceCode.add(new RateType(RateTypeList.get(item).getSourceCode(), RateTypeList.get(item)
					.getTargetCode()));
			updateChartsData();
			graphicalView.repaint();
		}

	}
}